package service

import (
	"context"
	"errors"
	v1 "gin-vben-admin/api/v1"
	"gin-vben-admin/api/v1/errcode"
	"gin-vben-admin/internal/repository"
	"gin-vben-admin/pkg/captcha"
	"gin-vben-admin/pkg/jwt"
	"golang.org/x/crypto/bcrypt"
)

type LoginService interface {
	Login(ctx context.Context, req *v1.LoginReq) (*v1.LoginRes, error)
	Logout(ctx context.Context, userId int64) (err error)
	RefreshToken(ctx context.Context, req *v1.RefreshTokenReq) (res *v1.LoginRes, err error)
	Profile(ctx context.Context, uid int64) (*v1.ProfileRes, error)
}

func NewLoginService(service *Service, userRepo repository.SysUserRepository, capt *captcha.Captcha) LoginService {
	return &loginService{
		userRepo: userRepo,
		Service:  service,
		captcha:  capt,
	}
}

type loginService struct {
	userRepo repository.SysUserRepository
	captcha  *captcha.Captcha
	*Service
}

func (s *loginService) Login(ctx context.Context, req *v1.LoginReq) (*v1.LoginRes, error) {
	//验证码
	if s.Service.config.OpenCaptcha && !s.captcha.Verify(req.CaptchaId, req.Captcha) {
		return nil, errcode.ErrBadCaptcha
	}
	m, err := s.userRepo.GetByEmailOrUsername(ctx, req.Email, req.Username)
	if err != nil {
		return nil, errcode.ErrUserOrPwd
	}
	if m == nil || m.Status.IsDisable() {
		return nil, errcode.ErrForbidden
	}
	if bcrypt.CompareHashAndPassword(m.Password, []byte(req.Password)) != nil {
		return nil, errcode.ErrUserOrPwd
	}
	//生成token
	res := v1.LoginRes{}
	res.Token, res.TokenExpire, err = s.jwt.CreateToken(jwt.TokenPayload{
		UserId:   m.UID,
		Email:    m.Email,
		Username: m.Username,
	})
	if err != nil {
		return nil, err
	}
	res.UserId = m.UID
	//记住登陆
	if req.Remember {
		res.RefreshToken, res.RefreshTokenExpire, err = s.jwt.CreateRefreshToken(jwt.TokenPayload{
			UserId:    m.UID,
			Email:     m.Email,
			Username:  m.NickName,
			IsRefresh: true,
		})
		if err != nil {
			return nil, err
		}
		m.RememberToken = res.RefreshToken
		if err = s.userRepo.Update(ctx, m, "remember_token"); err != nil {
			return nil, err
		}
	}
	return &res, nil
}

func (s *loginService) Profile(ctx context.Context, uid int64) (*v1.ProfileRes, error) {
	m, err := s.userRepo.GetByID(ctx, uid)
	if err != nil {
		return nil, err
	}
	return &v1.ProfileRes{
		UID:      m.UID,
		Email:    m.Email,
		Username: m.Username,
		NickName: m.NickName,
		Status:   m.Status,
	}, nil
}

// RefreshToken 刷新token
func (s *loginService) RefreshToken(ctx context.Context, req *v1.RefreshTokenReq) (res *v1.LoginRes, err error) {
	jwtClaims, err := s.jwt.ValidateToken(req.RefreshToken)
	if err != nil {
		return
	}
	m, err := s.userRepo.GetByID(ctx, jwtClaims.UserId)
	if err != nil {
		return
	}
	if m.Status.IsDisable() {
		return nil, errcode.ErrForbidden
	}
	if m.RememberToken != req.RefreshToken {
		return nil, errors.New("refresh token 错误")
	}

	res = &v1.LoginRes{}
	res.Token, res.TokenExpire, err = s.jwt.CreateToken(jwt.TokenPayload{
		UserId:   m.UID,
		Email:    m.Email,
		Username: m.Username,
	})
	if err != nil {
		return
	}
	res.UserId = m.UID
	res.RefreshToken, res.RefreshTokenExpire, err = s.jwt.CreateRefreshToken(jwt.TokenPayload{
		UserId:    m.UID,
		Email:     m.Email,
		Username:  m.Username,
		IsRefresh: true,
	})
	if err != nil {
		return nil, err
	}
	m.RememberToken = res.RefreshToken
	if err = s.userRepo.Update(ctx, m, "remember_token"); err != nil {
		return nil, err
	}
	return
}

// Logout 退出
func (s *loginService) Logout(ctx context.Context, userId int64) (err error) {
	m, err := s.userRepo.GetByID(ctx, userId)
	if err != nil {
		return
	}
	m.RememberToken = ""
	if err = s.userRepo.Update(ctx, m, "remember_token"); err != nil {
		return err
	}
	return nil
}
